#include <string>
#include <vector>
#include "StoreObjectMerge.h"
#include "PublicFunction.h"
#include "StoreObject.h"


using namespace std;

namespace gitstore {

template <>
ErrorStatus CStoreObjectMerge::merge<const std::string&>(const StoreObject& value1, const StoreObject& value2,
                                                         const StoreObject& value_ancestor, StoreObject& value_merge, const std::string& seperator) {
    std::vector<std::string> v1;
    std::vector<std::string> v2;
    std::vector<std::string> v_a;
    splitString(value1.get(), v1, seperator);
    splitString(value2.get(), v2, seperator);
    splitString(value_ancestor.get(), v_a, seperator);

    MyersDiffRes diff1, diff2;
    auto res = findDiffs(v1.begin(), v1.end(), v2.begin(),
                         v2.end(), v_a.begin(), v_a.end(), diff1, diff2);
    if ( Success != res ) {
        return res;
    }
    std::vector<std::string> v_m;
    res = mergeSequenceProcess(v1, v2, v_a, v_m, diff1, diff2);
    if ( Success != res ) {
        return res;
    }
    std::string s_res;
    s_res.reserve(1.3*(value1.get().size()>value2.get().size()?value1.get().size():value2.get().size()));
    for ( auto &line : v_m )
    {
        s_res += (line+seperator);
    }
    value_merge.set(std::move(s_res));
    return Success;
}

template <>
ErrorStatus CStoreObjectMerge::merge(const StoreObject& value1, const StoreObject& value2,
                                     const StoreObject& value_ancestor, StoreObject& value_merge) {
    if ( value1.getType() == StoreObjectType::Map ) {
        return mergeMapProcess(value1, value2, value_ancestor, value_merge);
    }
    else {
        return mergeListProcess(value1, value2, value_ancestor, value_merge);
    }
}

ErrorStatus CStoreObjectMerge::mergeMapProcess(const StoreObject& value1, const StoreObject& value2,
                                               const StoreObject& value_ancestor, StoreObject& value_merge) {
    value_merge.resetType(StoreObjectType::Map);
    // compare key and value
    for ( auto iter1 = value1.mbegin(); iter1 != value1.mend(); iter1++ ) {
        if ( !value2.isKeyExist(iter1->first) ) {
            value_merge[iter1->first] = iter1->second;
            continue;
        }
        if ( iter1->second.compare(value2[iter1->first]) == 0 ) {
            value_merge[iter1->first] = iter1->second;
            continue;
        } else {
            return MergeConflict;
        }
    }

    for ( auto iter2 = value2.mbegin(); iter2 != value1.mend(); iter2++ ) {
        if ( !value_merge.isKeyExist(iter2->first) ) {
            value_merge[iter2->first] = iter2->second;
            continue;
        }
    }

    return Success;
}

template<typename Iter>
ErrorStatus CStoreObjectMerge::findDiffs(Iter&& iter1_begin, Iter&& iter1_end, Iter&& iter2_begin,
                                         Iter&& iter2_end, Iter&& iter_ancestor_begin, Iter&& iter_ancestor_end,
                                         MyersDiffRes& diff1, MyersDiffRes& diff2) {
    diff1 = emattsan::myers::diff(iter_ancestor_begin, iter_ancestor_end, iter1_begin, iter1_end);
    diff2 = emattsan::myers::diff(iter_ancestor_begin, iter_ancestor_end, iter2_begin, iter2_end);
    diff1.push_back(emattsan::myers::COMMON);
    diff2.push_back(emattsan::myers::COMMON);
    return Success;
}

ErrorStatus CStoreObjectMerge::mergeListProcess(const StoreObject& value1, const StoreObject& value2,
                                                const StoreObject& value_ancestor, StoreObject& value_merge) {
    value_merge.clear();

    MyersDiffRes diff1, diff2;
    auto res = findDiffs(value1.vbegin(), value1.vend(), value2.vbegin(),
                         value2.vend(), value_ancestor.vbegin(), value_ancestor.vend(), diff1, diff2);

    if (Success != res)
        return res;
    res = mergeSequenceProcess(value1, value2, value_ancestor, value_merge, diff1, diff2);
    return res;
}

template<typename T>
ErrorStatus CStoreObjectMerge::mergeSequenceProcess(const T& value1, const T& value2, const T& value_ancestor, T& value_out, const MyersDiffRes &diff1, const MyersDiffRes &diff2)
{
    value_out.clear();
//    value_out.reserve(diff1.size()>diff2.size()?diff1.size():diff2.size());
    int common_delete_1 = -1;
    int common_delete_pre_1 = -1;
    int common_delete_2 = -1;
    int common_delete_pre_2 = -1;
    int new_1 = -1;
    int new_pre_1 = -1;
    int new_2 = -1;
    int new_pre_2 = -1;
    auto iter1 = diff1.begin();
    auto iter2 = diff2.begin();
    while ( iter1 != diff1.end() )
    {
        if (( *iter1 == emattsan::myers::COMMON ) || ( *iter1 == emattsan::myers::DELETE )) {
            common_delete_1++;
            if( *iter1 == emattsan::myers::COMMON )
                new_1++;
            while ( common_delete_2 != common_delete_1 )
            {
                if ( *iter2 == emattsan::myers::COMMON )
                {
                    common_delete_2++;
                    new_2++;
                }
                else if ( *iter2 == emattsan::myers::DELETE )
                {
                    common_delete_2++;

                }
                else if ( *iter2 == emattsan::myers::ADD)
                {
                    new_2++;
                }
                iter2++;
            }
            if  ( ( new_1 - new_pre_1 ) == 1 )
            {
                for ( auto i = new_pre_2+1 ; i<new_2 ; i++ )
                {
                    value_out.emplace_back(value2.at(i));
                }
            }
            else if ( ( new_2 - new_pre_2 ) == 1 )
            {
                for ( auto i = new_pre_1+1 ; i<new_1 ; i++ )
                {
                    value_out.emplace_back(value1.at(i));
                }
            }
            else if ( ( new_2 - new_pre_2 ) == ( new_1 - new_pre_1 ))
            {
                for( auto i = new_pre_1+1,j = new_pre_2+1 ; i<new_1&&j<new_2 ; i++,j++ )
                {
                    if ( value1.at(i) == value2.at(j) )
                        value_out.emplace_back(value1.at(i));
                    else {
                        return MergeConflict;
                    }
                }
            }
            else if ( ( new_2 - new_pre_2 ) < ( new_1 - new_pre_1 ) )
            {
                int i = new_pre_1+1;
                for( auto j = new_pre_2+1 ; j<new_2 ; i++,j++ )
                {
                    if ( value1.at(i) == value2.at(j) )
                        value_out.emplace_back(value1.at(i));
                    else {
                        return MergeConflict;
                    }
                }

                for (; i<new_1 ; i++)
                {
                    value_out.emplace_back(value1.at(i));
                }

            }
            else if ( ( new_2 - new_pre_2 ) > ( new_1 - new_pre_1 ) )
            {
                int i = new_pre_2+1;
                for( auto j = new_pre_1+1 ; j<new_1 ; i++,j++ )
                {
                    if ( value2.at(i) == value1.at(j) )
                        value_out.emplace_back(value2.at(i));
                    else {
                        return MergeConflict;
                    }
                }

                for (; i<new_2 ; i++)
                {
                    value_out.emplace_back(value2.at(i));
                }
            }

            common_delete_pre_1 = common_delete_1;
            common_delete_pre_2 = common_delete_2;
            new_pre_1 = new_1;
            new_pre_2 = new_2;
            if (( *iter1 == emattsan::myers::COMMON) && (*(iter2-1) == emattsan::myers::COMMON)) {
                if ( common_delete_1 < value_ancestor.size() )
                    value_out.emplace_back(value_ancestor.at(common_delete_1));
            }
        }
        //        else if ( *iter1 == emattsan::myers::DELETE )
        //        {
        //            common_delete_1++;
        //            while ( common_delete_2 != common_delete_1 )
        //            {
        //                if ( *iter2 == emattsan::myers::COMMON )
        //                {
        //                    common_delete_2++;
        //                    new_2++;
        //                }
        //                else if ( *iter2 == emattsan::myers::DELETE )
        //                {
        //                    common_delete_2++;

        //                }
        //                else if ( *iter2 == emattsan::myers::ADD)
        //                {
        //                    new_2++;
        //                }
        //                iter2++;
        //            }
        //            common_delete_pre_1 = common_delete_1;
        //            common_delete_pre_2 = common_delete_2;
        //            new_pre_1 = new_1;
        //            new_pre_2 = new_2;
        //        }
        else if ( *iter1 == emattsan::myers::ADD )
        {
            new_1++;
        }
        iter1++;
    }
    return Success;
    //    int common1 = -1;
    //    int common2 = -1;

    //    int common_new1 = -1;
    //    int common_new2 = -1;


    //    //find common line no
    //    std::vector<int> base1;
    //    std::vector<int> deletebase1;
    //    std::vector<int> newbase1;
    //    for ( auto &t1 : diff1 )
    //    {
    //        if ( t1 == emattsan::myers::COMMON ) {
    //            common1++;
    //            common_new1++;
    //            base1.emplace_back(common1);
    //            newbase1.emplace_back(common_new1);
    //        }
    //        else if ( t1 == emattsan::myers::DELETE )
    //        {
    //            common1++;
    //            deletebase1.emplace_back(common1);
    //        }
    //        else if ( t1 == emattsan::myers::ADD )
    //        {
    //            common_new1++;
    //        }
    //    }

    //    std::vector<int> base2;
    //    std::vector<int> deletebase2;
    //    std::vector<int> newbase2;
    //    for ( auto &t2 : diff2 )
    //    {
    //        if ( t2 == emattsan::myers::COMMON ) {
    //            common2++;
    //            common_new2++;
    //            base2.emplace_back(common2);
    //            newbase2.emplace_back(common_new2);
    //        }
    //        else if ( t2 == emattsan::myers::DELETE )
    //        {
    //            common2++;
    //            deletebase2.emplace_back(common2);
    //        }
    //        else if ( t2 == emattsan::myers::ADD )
    //        {
    //            common_new2++;
    //        }
    //    }

    //    size_t s1=0, s2=0;
    //    int common_last1 = -1;
    //    int common_last2 = -1;
    //    while ( s1<base1.size() && s2<base2.size() )
    //    {
    //        if ( base1.at(s1) == base2.at(s2) )
    //        {
    //            if ( common_last1 < 0 )
    //            {
    //                if ( base1.at(s1) == 0 )
    //                {
    //                    common_last1 = 0;
    //                    common_last2 = 0;
    //                }
    //                else {
    //                    if ( newbase1.at(s1) == 0 )
    //                    {
    //                        for ( auto ns2 = 0 ; ns2<newbase2.at(s2) ; ns2++ )
    //                        {

    //                            value_out.emplace_back(value2.at(ns2));
    //                        }
    //                    }
    //                    if ( newbase2.at(s2) == 0 )
    //                    {
    //                        for ( auto ns1 = 0 ; ns1<newbase1.at(s1) ; ns1++ )
    //                        {
    //                            value_out.emplace_back(value1.at(ns1));
    //                        }
    //                    }
    //                    if ( newbase2.at(s2) != newbase1.at(s1) )
    //                    {
    //                        return -1;
    //                    }
    //                    for ( auto ns1 = 0 ; ns1 < newbase1.at(s1); ns1++ )
    //                    {
    //                        if ( value1.at(ns1) != value2.at(ns1) )
    //                        {
    //                            return -1;
    //                        }
    //                        value_out.emplace_back(value1.at(ns1));
    //                    }
    //                }

    //            }
    //            else {
    //                if ( ( newbase1.at(s1) - newbase1.at(common_last1) ) == 1 )
    //                {
    //                    for ( auto n = newbase2.at(common_last2)+1 ; n<newbase2.at(s2) ; n++ )
    //                    {
    //                        if ( std::find(deletebase1.begin(), deletebase1.end(), n) == deletebase1.end() )
    //                            value_out.emplace_back(value2.at(n));
    //                    }
    //                }
    //                else if ( ( newbase2.at(s2) - newbase2.at(common_last2) ) == 1 )
    //                {
    //                    for ( auto n = newbase1.at(common_last1)+1 ; n<newbase1.at(s1) ; n++ )
    //                    {
    //                        if ( std::find(deletebase2.begin(), deletebase2.end(), n) == deletebase2.end() )
    //                            value_out.emplace_back(value1.at(n));
    //                    }
    //                }
    //                else if ( ( newbase1.at(s1) - newbase1.at(common_last1) ) != ( newbase2.at(s2) - newbase2.at(common_last2) )  )
    //                {
    //                    return -1;
    //                }
    //                else {
    //                    for ( auto ns1 = newbase1.at(common_last1)+1 ; ns1 < newbase1.at(s1); ns1++ )
    //                    {
    //                        if ( value1.at(ns1) != value2.at(ns1) )
    //                        {
    //                            return -1;
    //                        }
    //                        value_out.emplace_back(value1.at(ns1));
    //                    }
    //                }
    //            }
    //            value_out.emplace_back(value_ancestor.at(base1.at(s1)));
    //            common_last1 = s1;
    //            common_last2 = s2;
    //            s1++;
    //            s2++;
    //        }
    //        else if ( base1.at(s1) < base2.at(s2) )
    //        {
    //            s1++;
    //        }
    //        else if ( base1.at(s1) > base2.at(s2) )
    //        {
    //            s2++;
    //        }
    //    }
    //    return 0;
}
}
